home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 June: Reference Library / Dev.CD Jun 99 RL Disk 1.toast / Technical Documentation / Develop / develop Issue 27 / develop Issue 27 code / Speech Recognition Sample / SRSample.c next >
Encoding:
C/C++ Source or Header  |  1996-06-19  |  22.8 KB  |  826 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        SRSample.c
  3.  
  4.     Contains:    Sample code illustrating basic use of the
  5.                 Speech Recognition Manager.
  6.     
  7.     Written by:    Arlo Reeves & Matt Pallakoff, Apple Computer, Inc.
  8. */
  9.  
  10. #include "SpeechRecognition.h"
  11. #include <Types.h>
  12. #include <Memory.h>
  13. #include <Quickdraw.h>
  14. #include <TextEdit.h>
  15. #include <Dialogs.h>
  16. #include <OSUtils.h>
  17. #include <ToolUtils.h>
  18. #include <Menus.h>
  19. #include <Fonts.h>
  20. #include <Events.h>
  21. #include <Resources.h>
  22. #include <Files.h>
  23. #include <Errors.h>
  24. #include <AppleEvents.h>
  25. #include <Gestalt.h>
  26. #include <string.h>
  27.  
  28. /* 'DITL' ID's for main dialog */
  29.  
  30. enum {
  31.     kQuitButton = 1,
  32.     kWhatYouCanSayStTx,
  33.     kGrammarStTx,
  34.     kWhatWasHeardStTx,
  35.     kResultTextStTx
  36. };
  37.  
  38. /* constants */
  39.  
  40. enum {
  41.     kDialogResID                 =    128,
  42.  
  43.     kTopLMRefcon                =    100,
  44.     kHelloRefCon                =    0,                /* distinguishes between different results */
  45.     kGoodbyeRefCon,
  46.     kWhatTimeIsItRefCon,
  47.     kCompanyRefCon,
  48.     kAppleRefCon                =    0,
  49.     kNetscapeRefCon,
  50.     kCocaColaRefCon,
  51.     
  52.     kErrorAlertResID            =    129,
  53.     kErrStringType                =    'Estr',
  54.     kInternalError                =    100,
  55.     kBadSRMVersion
  56. };
  57.  
  58. /* globals */
  59. DialogPtr            gDialog = NULL;
  60.  
  61. SRRecognitionSystem    gRecognitionSystem        = NULL;    /* our speech recognition system */
  62. SRRecognizer        gRecognizer                = NULL;    /* our recognizer */
  63. SRLanguageModel        gTopLanguageModel        = NULL;    /* our language model - what we're listening for */
  64. AEEventHandlerUPP    gAERoutineDescriptor    = NULL;    /* our AppleEvent routine descriptor */
  65.  
  66. /* prototypes */
  67.  
  68. void            main(void);
  69. OSErr             DoMainEventLoop (void);
  70. Boolean         HasValidSpeechRecognitionVersion (void);
  71. OSErr             InitSpeechRecognition (void);
  72. OSErr             TerminateSpeechRecognition (void);
  73. OSErr            BuildLanguageModel (void);
  74. OSErr            AddFancierLanguageModel (SRLanguageModel baseLM);
  75. pascal OSErr     HandleRecognitionDoneAE (AppleEvent *theAEevt, AppleEvent *reply, long refcon);
  76. OSErr            ProcessRecognitionResult (SRRecognitionResult result, SRRecognizer recognizer);
  77. OSErr            ProcessFancierLanguageModel (SRPath resultPath, SRRecognizer recognizer);
  78. OSErr             DisplayRecognizedText (SRRecognitionResult result);
  79. void             ErrorAlert (OSErr macErr);
  80. OSErr             DisableStockPath ();
  81. OSErr            RefillCompanyLM ();
  82. OSErr             SaveTopLanguageModel ();
  83.  
  84. /*    main entry point
  85.  
  86.     Remember, this application is meant to show you how to use the 
  87.     Speech Recognition Manager, not to show good human interface programming. 
  88.     The following code to manage the dialog is not meant to be a good example 
  89.     of application design.
  90. */
  91.  
  92. void main(void)
  93. {
  94.     OSErr            status = noErr;
  95.  
  96.     /* initialize the world */
  97.  
  98.     InitGraf((Ptr) &qd.thePort);
  99.     InitFonts();
  100.     InitWindows();
  101.     InitMenus();
  102.     TEInit();
  103.     InitDialogs(nil);
  104.     InitCursor();
  105.     MaxApplZone();
  106.  
  107.     status = InitSpeechRecognition ();
  108.     
  109.     if (!status) {
  110.         status = BuildLanguageModel ();
  111.         
  112.         if (!status)
  113.             status = SRSetLanguageModel (gRecognizer, gTopLanguageModel);
  114.  
  115.         if (!status) {
  116.             status = SRStartListening (gRecognizer);
  117.             
  118.             if (!status) status = DoMainEventLoop ();    /* ignore status */
  119.                 
  120.             status = SRStopListening (gRecognizer);
  121.         }
  122.         
  123.         TerminateSpeechRecognition ();                    /* ignore status */
  124.     }
  125.     
  126.     if (status)
  127.         ErrorAlert (status);
  128. }
  129.  
  130.  
  131. /*    DoMainEventLoop
  132.  
  133.     Brings up our simple dialog.  Then processes the events as they come.
  134. */
  135.  
  136. OSErr DoMainEventLoop (void)
  137. {
  138.     OSErr            status = noErr;
  139.     GrafPtr            oldPort;
  140.     
  141.     /* setup main window */
  142.     GetPort (&oldPort);
  143.  
  144.     gDialog = GetNewDialog (kDialogResID, nil, (WindowPtr) -1);
  145.     if (gDialog) {
  146.         Boolean            done = false;
  147.         SetPort (gDialog);
  148.         ShowWindow (gDialog);
  149.  
  150.         /* main event loop */
  151.         while (!done) {
  152.             DialogPtr dlog;
  153.             EventRecord event;
  154.  
  155.             if (WaitNextEvent (everyEvent, &event, 10, NULL)) {
  156.  
  157.                 /* if a dialog event, do the dialog thing... */
  158.                 if (IsDialogEvent (&event)) {
  159.                     short itemHit;
  160.                     
  161.                     if (DialogSelect (&event, &dlog, &itemHit)) {
  162.                         switch (itemHit) {
  163.                             case kQuitButton:
  164.                                 done = true;
  165.                                 break;
  166.                         }
  167.                     }
  168.                 }
  169.                 
  170.                 /* ...otherwise it's a normal event */
  171.                 else {
  172.                     switch (event.what) {
  173.                         case nullEvent:
  174.                         case mouseDown:
  175.                             {
  176.                                 short partCode;
  177.                                 switch (partCode = FindWindow (event.where, &dlog)) {
  178.                                     case inDrag:
  179.                                         DragWindow (dlog, event.where, &qd.screenBits.bounds);
  180.                                         break;
  181.                                 }
  182.                             }
  183.                             break;
  184.                         case kHighLevelEvent:
  185.                             AEProcessAppleEvent (&event);
  186.                             break;
  187.                         case keyDown:
  188.                         case autoKey:                      
  189.                         case diskEvt:
  190.                         default:
  191.                             break;
  192.                     }
  193.                 }
  194.             }
  195.         }
  196.         DisposeDialog (gDialog);
  197.     }
  198.     else status = kInternalError;
  199.  
  200.     SetPort(oldPort);
  201.     
  202.     return status;
  203. }
  204.  
  205. /*    HasValidSpeechRecognitionVersion
  206.  
  207.     This routine checks the Speech Recognition Manager version and makes sure the version 
  208.     is greater than or equal to the version needed for this program to run.  Only Speech
  209.     Recognition Manager versions >= 1.5 adhere to the publicly defined API.
  210. */
  211.  
  212. Boolean HasValidSpeechRecognitionVersion (void)
  213. {
  214.     OSErr                    status;
  215.     long                    theVersion;
  216.     Boolean                 validVersion                = false;
  217.     const unsigned long     kMinimumRequiredSRMVersion    = 0x00000150;
  218.     
  219.     status = Gestalt (gestaltSpeechRecognitionVersion, &theVersion);
  220.     
  221.     if (!status)
  222.         if (theVersion >= kMinimumRequiredSRMVersion)
  223.             validVersion = true;
  224.             
  225.     return validVersion;
  226. }
  227.  
  228. /*    InitSpeechRecognition
  229.  
  230.     If the Speech Recognition Manager version is valid, this routine opens a recognition system
  231.     and a recognizer, setting the kSRFeedbackAndListeningModes property to kSRHasFeedbackHasListenModes
  232.     so that SRSample will have a Speech Recognition feedback window.
  233. */
  234.  
  235. OSErr InitSpeechRecognition (void)
  236. {
  237.     OSErr status = kBadSRMVersion;
  238.     
  239.     /* Ensure that the Speech Recognition Mgr is available. */
  240.     if (HasValidSpeechRecognitionVersion ())  {
  241.     
  242.         /* Open the default recognition system. */
  243.         status = SROpenRecognitionSystem (&gRecognitionSystem, kSRDefaultRecognitionSystemID);
  244.         
  245.         /* Use standard feedback window and listening modes. */
  246.         if (!status) {
  247.             short feedbackNeeded = kSRHasFeedbackHasListenModes;
  248.             
  249.             status = SRSetProperty (gRecognitionSystem, kSRFeedbackAndListeningModes, &feedbackNeeded, sizeof(feedbackNeeded));
  250.         }
  251.         
  252.         /* Create a new recognizer */
  253.         if (!status)
  254.             status = SRNewRecognizer (gRecognitionSystem, &gRecognizer, kSRDefaultSpeechSource);
  255.  
  256.         /* install our Apple Event handler for recognition results */            
  257.         if (!status) {
  258.             status = memFullErr;
  259.             gAERoutineDescriptor = NewAEEventHandlerProc (HandleRecognitionDoneAE);
  260.             
  261.             if (gAERoutineDescriptor)
  262.                 status = AEInstallEventHandler (kAESpeechSuite, kAESpeechDone, gAERoutineDescriptor, 0, false);
  263.         }
  264.     }
  265.  
  266.     return status;
  267. }
  268.  
  269.  
  270. /*    TerminateSpeechRecognition
  271.  
  272.     Close down the Recognizer and RecognitionSystem allocated in InitSpeechRecognition.
  273.     Stop listening before releasing the recognizer just for thoroughness (it's harmless
  274.     if we aren't listening).
  275.     
  276.     status is ignored througout.
  277. */
  278.  
  279. OSErr TerminateSpeechRecognition (void)
  280. {
  281.     OSErr status = noErr;
  282.     
  283.     /* if we have a top level language model, release it */
  284.     if (gTopLanguageModel) {
  285.         status = SRReleaseObject (gTopLanguageModel);            /* ignore status */
  286.         gTopLanguageModel = NULL;
  287.     }
  288.     
  289.     /* if we have a recognizer, release it */
  290.     if (gRecognizer) {
  291.         status = SRStopListening (gRecognizer);
  292.         status = SRReleaseObject (gRecognizer);
  293.         gRecognizer = NULL;
  294.     }
  295.  
  296.     /* if we have a recognition system, close it */
  297.     if (gRecognitionSystem) {
  298.         status = SRCloseRecognitionSystem (gRecognitionSystem);
  299.         gRecognitionSystem = NULL;
  300.     }
  301.     
  302.     /* remove our AppleEvent handler and dispose of the handler's routine descriptor */
  303.     if (gAERoutineDescriptor) {
  304.         status = AERemoveEventHandler (kAESpeechSuite, kAESpeechDone, gAERoutineDescriptor, false);
  305.         DisposeRoutineDescriptor (gAERoutineDescriptor);
  306.         gAERoutineDescriptor = NULL;
  307.     }
  308.  
  309.     return status;
  310. }
  311.  
  312. /*    BuildLanguageModel
  313.  
  314.     Allocates and builds gTopLanguageModel to be
  315.     
  316.     Hello                or
  317.     Goodbye                or
  318.     What time is it?
  319.     
  320.     Using the simplest technique, SRAddText.  It then augments this simple language
  321.     model with a fancier one by calling AddFancierLanguageModel.
  322. */
  323.  
  324. OSErr BuildLanguageModel (void)
  325. {    
  326.     OSErr            status        = noErr;
  327.     const char        kLMName[]    = "<Top LM>";
  328.  
  329.     /* first, allocate the language model, storing it in gTopLanguageModel */
  330.     status = SRNewLanguageModel (gRecognitionSystem, &gTopLanguageModel, kLMName, strlen (kLMName));
  331.     
  332.     if (!status) {
  333.         long refcon = kTopLMRefcon;
  334.         
  335.         /* set the refcon of our top language model so that when we process our recognition */
  336.         /* result we will be able to distinguish it from the rejection word, "???" */
  337.         status = SRSetProperty (gTopLanguageModel, kSRRefCon, &refcon, sizeof (refcon));
  338.  
  339.         if (!status) {
  340.             const char    *kSimpleStr[]    = { "Hello", "Goodbye", "What time is it?", NULL };
  341.             char         **currentStr     = (char **) kSimpleStr;
  342.             long        refcon             = kHelloRefCon;
  343.             
  344.             /* Add each of the strings in kSimpleStr to the language model, and set the refcon to */
  345.             /* the index of the string in the kSimpleStr array. Note that SRAddText is a shortcut */
  346.             /* for calling SRNewPhrase, SRAddLanguageObject, and SRReleaseObject in succession */
  347.             
  348.             while (*currentStr && !status) {
  349.                 status = SRAddText (gTopLanguageModel, *currentStr, strlen (*currentStr), refcon++);
  350.                 ++currentStr;
  351.             }
  352.             
  353.             /* now augment this simple language model with a fancier one */
  354.             if (!status)
  355.                 status = AddFancierLanguageModel (gTopLanguageModel);
  356.         }
  357.     }
  358.     
  359.     return status;
  360. }
  361.  
  362. /*    AddFancierLanguageModel
  363.  
  364.     Augment the language model built in BuildSimpleLanguageModel to have a fourth
  365.     phrase which includes an embedded language model and an optional word:
  366.  
  367.         Tell me the price of <company> [stock]
  368.  
  369.     where
  370.  
  371.         <company> = Apple | Netscape | Coca Cola
  372.     
  373.     is the embedded language model and [stock] indicates that the word 'stock' 
  374.     is optional.
  375. */
  376.  
  377. OSErr AddFancierLanguageModel (SRLanguageModel baseLM)
  378. {
  379.     OSErr status = noErr;
  380.     
  381.     /*    First create a new SRPath, to which we will add the phrase "Tell me the price of" */
  382.     /*    the language model "<company>" and the word "stock".  We'll then add this path to */
  383.     /*    the baseLM */
  384.  
  385.     SRPath stockPath;
  386.     
  387.     status = SRNewPath (gRecognitionSystem, &stockPath);
  388.     
  389.     if (!status) {
  390.         const char        kWhatString[]    = "Tell me the price of";
  391.         SRPhrase         whatPhrase        = NULL;
  392.         SRLanguageModel    companyLM        = NULL;
  393.         SRWord             stockWord        = NULL;
  394.         long            refcon            = kCompanyRefCon;
  395.  
  396.         /* set the refcon now to help us distinguish what was said later */        
  397.         status = SRSetProperty (stockPath, kSRRefCon, &refcon, sizeof (refcon));
  398.         
  399.         if (!status) {
  400.             /* allocate the phrase "Tell me the price of" */
  401.             status = SRNewPhrase (gRecognitionSystem, &whatPhrase, kWhatString, strlen (kWhatString));
  402.  
  403.             /* add the whatPhrase to the stockPath */        
  404.             if (!status) {
  405.                 status = SRAddLanguageObject (stockPath, whatPhrase);
  406.                 
  407.                 /* once added, the stockPath retains a reference to the whatPhrase */
  408.                 /* so we can release the whatPhrase now */
  409.                 if (!status) {
  410.                     status = SRReleaseObject (whatPhrase);
  411.                     whatPhrase = NULL;
  412.                 }
  413.             }
  414.         }
  415.         
  416.         /* create the embedded language model <company> and add it to the stockPath */
  417.         if (!status) {
  418.             const char        kCompanyLMName[] = "<company LM>";
  419.             
  420.             /* allocate the embedded language model '<company>' */
  421.             status = SRNewLanguageModel (gRecognitionSystem, &companyLM, kCompanyLMName, strlen (kCompanyLMName));
  422.             
  423.             if (!status) {
  424.                 const char        *kCompanyNames[] = { "Apple", "Netscape", "Pepsi", NULL };
  425.                 char            **companyName = (char **) kCompanyNames;
  426.                 long            refcon = kAppleRefCon;
  427.                 
  428.                 /* add the compnay names to the language model '<company>' */
  429.                 while (*companyName && !status) {
  430.                     status = SRAddText (companyLM, *companyName, strlen (*companyName), refcon++);
  431.                     ++companyName;
  432.                 }
  433.                 
  434.                 /* add the company language model to the stockPath... */
  435.                 if (!status) {
  436.                     status = SRAddLanguageObject (stockPath, companyLM);
  437.                     
  438.                     /* ... and release it since it is now 'owned' by the stockPath */
  439.                     if (!status) {
  440.                         status = SRReleaseObject (companyLM);
  441.                         companyLM = NULL;
  442.                     }
  443.                 }
  444.             }
  445.         }
  446.         
  447.         /* create the word 'stock', make it optional and add it to the stockPath */
  448.         if (!status) {
  449.             const char    kStockWord[] = "stock";
  450.             
  451.             /* allocated the word */
  452.             status = SRNewWord (gRecognitionSystem, &stockWord, kStockWord, strlen (kStockWord));
  453.             
  454.             if (!status) {
  455.                 Boolean wantOptional = true;
  456.                 
  457.                 /* make it optional by setting it's kSROptional property to true */
  458.                 status = SRSetProperty (stockWord, kSROptional, &wantOptional, sizeof (wantOptional));
  459.  
  460.                 /* add it to the stockPath */                
  461.                 if (!status) {
  462.                     status = SRAddLanguageObject (stockPath, stockWord);
  463.                     
  464.                     /* ... and release our reference to it */
  465.                     if (!status) {
  466.                         status = SRReleaseObject (stockWord);
  467.                         stockWord = NULL;
  468.                     }
  469.                 }
  470.             }
  471.         }
  472.         
  473.         
  474.         /* add the stock path to the baseLM and we're done */
  475.         if (!status) {
  476.             status = SRAddLanguageObject (baseLM, stockPath);
  477.             
  478.             /* and release our reference to the path now that we're done with it */
  479.             if (!status) {
  480.                 status = SRReleaseObject (stockPath);
  481.                 stockPath = NULL;
  482.             }
  483.         }
  484.         
  485.         /* if we had any problems along the way, clean up here */
  486.         if (status) {
  487.             if (whatPhrase) SRReleaseObject (whatPhrase);    /* status ignored */
  488.             if (companyLM) SRReleaseObject (companyLM);
  489.             if (stockWord) SRReleaseObject (stockWord);
  490.             if (stockPath) SRReleaseObject (stockPath);
  491.         }
  492.     }
  493.     
  494.     return status;
  495. }
  496.  
  497.  
  498. /*    HandleRecognitionDoneAE
  499.  
  500.     This is our AppleEvent handler for the kSRRecognitionDone AppleEvent.
  501.     It extracts the recognition status code, and if there's something to
  502.     process, it extracts the recognizer and recognition result objects from
  503.     the AppleEvent.  These are then process in ProcessRecognitionResult
  504. */
  505.  
  506. pascal OSErr HandleRecognitionDoneAE (AppleEvent *theAEevt, AppleEvent *reply, long refcon)
  507. {
  508.     OSErr recognitionStatus = 0, status;
  509.     long actualSize;
  510.     DescType actualType;
  511.     
  512.     /* Get recognition result status. */
  513.     status = AEGetParamPtr (theAEevt, keySRSpeechStatus, typeShortInteger, &actualType, (Ptr) &recognitionStatus, sizeof (recognitionStatus), &actualSize);
  514.     
  515.     /* Get the SRRecognizer */
  516.     if (!status && !recognitionStatus) {
  517.         SRRecognizer recognizer;
  518.         status = AEGetParamPtr (theAEevt, keySRRecognizer, typeSRRecognizer, &actualType, (Ptr) &recognizer, sizeof (recognizer), &actualSize);
  519.         
  520.         /* Get the SRRecognitionResult */
  521.         if (!status) {
  522.             SRRecognitionResult recResult;
  523.             status = AEGetParamPtr (theAEevt, keySRSpeechResult, typeSRSpeechResult, &actualType, (Ptr) &recResult, sizeof (recResult), &actualSize);
  524.             
  525.             /* extract the language model from the result */
  526.             if (!status) {
  527.                 SRLanguageModel resultLM;
  528.                 long propertySize = sizeof (resultLM);
  529.                 
  530.                 /* first get the text from the result and display it in our dialog */
  531.                 status = DisplayRecognizedText (recResult);
  532.                 
  533.                 /* get the language model out of the recognition result */                
  534.                 if (!status)
  535.                     status = SRGetProperty (recResult, kSRLanguageModelFormat, &resultLM, &propertySize);
  536.  
  537.                 /* process the language model */
  538.                 if (!status) {
  539.                     status = ProcessRecognitionResult (resultLM, recognizer);
  540.                 
  541.                     /* what we SRGot… we must SRRelease…! */
  542.                     SRReleaseObject (resultLM);
  543.                 }
  544.                 /* Also release the recognition result */
  545.                 SRReleaseObject (recResult);
  546.             }
  547.         }
  548.     }
  549.     
  550.     /* if recognition went fine, how about the processing? */
  551.     return recognitionStatus ? recognitionStatus : status;
  552. }
  553.  
  554. /*    ProcessRecognitionResult
  555.  
  556.     Process the recogition result here.  Get the kSRLMFormat (SRLangugaeModel) property from
  557.     the recogntion result; it is a SRLanguageModel object whose contents correspond to the words
  558.     that were heard.  By inspecting the refcon of the first item in this language model, we
  559.     the first item contained in this 
  560. */
  561.  
  562. OSErr ProcessRecognitionResult (SRLanguageModel resultLM, SRRecognizer recognizer)
  563. {
  564.     OSErr    status = noErr;
  565.     
  566.     if (resultLM && recognizer) {
  567.         long refcon;
  568.         long propertySize = sizeof (refcon);
  569.         
  570.         status = SRGetProperty (resultLM, kSRRefCon, &refcon, &propertySize);
  571.         
  572.         /* is the resultLM a subset of our top language model or is it the rejection word, "???" ? */
  573.         if (!status && refcon == kTopLMRefcon) {
  574.             SRLanguageObject languageObject;
  575.             propertySize = sizeof (languageObject);
  576.  
  577.             /* The resultLM contains either an SRPath or an SRPhrase.  The SRPhrases in gTopLanguageModel */
  578.             /* were created by the SRAddText calls in BuildLanguageModel.  The SRPath in */
  579.             /* gTopLanguageModel was created in AddFancierLanguageModel.  We use the refcon property */
  580.             /* we set in our language model building routines to distinguish between the results */
  581.             
  582.             /* We expect our result language model to contain only 1 item, a phrase or a path; get it */
  583.             status = SRGetIndexedItem (resultLM, &languageObject, 0);
  584.             
  585.             if (!status) {
  586.                 long refcon;
  587.                 propertySize = sizeof (refcon);
  588.                 
  589.                 /* get the refcon of the object at the root of our language model */
  590.                 status = SRGetProperty (languageObject, kSRRefCon, &refcon, &propertySize);
  591.                 
  592.                 if (!status) switch (refcon) {
  593.                     case kHelloRefCon:
  594.                     case kGoodbyeRefCon:
  595.                     case kWhatTimeIsItRefCon: 
  596.                         {
  597.                             const char *kResponses[] = 
  598.                                 {    "Hi There!", 
  599.                                     "Don't leave now!",
  600.                                     "It's time to use the Speech Recognition Manager!" 
  601.                                 };
  602.                             /* speak and display our response using the feedback character. Use */
  603.                             /* the refcon as an index into our response array */
  604.                             status = SRSpeakAndDrawText (recognizer, kResponses[refcon], strlen (kResponses[refcon]));
  605.                         }
  606.                         break;
  607.                     case kCompanyRefCon:
  608.                         status = ProcessFancierLanguageModel (languageObject, recognizer);
  609.                         break;
  610.                 }
  611.                 
  612.                 /* always SRRelease… what we SRGot… */
  613.                 status = SRReleaseObject (languageObject);
  614.             }
  615.         }
  616.     }
  617.     
  618.     return status;
  619. }
  620.  
  621. /*    ProcessFancierLanguageModel
  622.     
  623.     The SRPath contains 2 or 3 items: a phrase "Tell me the price of", an embedded
  624.     language model <company> and perhaps the word 'stock'.  Our response depends only
  625.     on the name of the company uttered.  The company name will always be the second
  626.     item in the path, so we access it directly and respond appropriately.
  627. */
  628.  
  629. OSErr ProcessFancierLanguageModel (SRPath resultPath, SRRecognizer recognizer)
  630. {
  631.     OSErr status = noErr;
  632.     
  633.     if (resultPath && recognizer) {
  634.         SRLanguageModel companyLM;
  635.         
  636.         /* get the second item in the path — it's the company language model  */
  637.         status = SRGetIndexedItem (resultPath, &companyLM, 1);
  638.         
  639.         if (!status && companyLM) {
  640.             SRPhrase companyName;
  641.             
  642.             /* the company language model will contain only 1 phrase; get it. */
  643.             status = SRGetIndexedItem (companyLM, &companyName, 0);
  644.             
  645.             if (!status) {
  646.                 long    refcon;
  647.                 long    propertySize = sizeof (refcon);
  648.                 
  649.                 /* get the refcon from the company name; it's our index into the response array */
  650.                 status = SRGetProperty (companyName, kSRRefCon, &refcon, &propertySize);
  651.                 
  652.                 if (!status) {
  653.                     const char *kResponses[] = {     "Apple stock is priced to move!",
  654.                                                     "Netscape is trading at fifty dollars.",
  655.                                                     "Why would you want to know that?"
  656.                                                 };
  657.                     status = SRSpeakAndDrawText (recognizer, kResponses[refcon], strlen (kResponses[refcon]));
  658.                 }
  659.                 
  660.                 /* what we SRGot… we must SRRelease… */
  661.                 status = SRReleaseObject (companyName);
  662.             }
  663.             
  664.             status = SRReleaseObject (companyLM);
  665.         }
  666.     }
  667.  
  668.     return status;
  669. }
  670.  
  671.  
  672. /*    DisplayRecognizedText
  673.  
  674.     This routine extracts the text from the recognition result, whatever that text
  675.     might be, and displays it in our dialog.
  676. */
  677.  
  678. OSErr DisplayRecognizedText (SRRecognitionResult result)
  679. {
  680.     OSErr status = noErr;
  681.     
  682.     /* first get the text from the result and display it in our dialog */
  683.     if (result) {
  684.         Str255    resultText;
  685.         long    resultTextSize = sizeof (resultText) - 1;
  686.         
  687.         status = SRGetProperty (result, kSRTEXTFormat, &resultText[1], &resultTextSize);
  688.         
  689.         if (!status) {
  690.             short    iType;
  691.             Handle    iHandle;
  692.             Rect    iRect;
  693.             
  694.             /* create pascal string of result text */
  695.             resultText[0] = (char) resultTextSize;    
  696.             
  697.             GetDialogItem (gDialog, kResultTextStTx, &iType, &iHandle, &iRect);
  698.             SetDialogItemText (iHandle, resultText);                    /* this handles redraw */
  699.         }
  700.     }
  701.     
  702.     return status;
  703. }
  704.  
  705.  
  706. /*    ErrorAlert
  707.  
  708.     Looks for an 'Estr' resource with ID macErr and provides a default error
  709.     string if none is found, then posts an alert forcing the user to quit.
  710. */
  711.  
  712. void ErrorAlert (OSErr macErr)
  713. {
  714.     StringHandle    errMsg;
  715.     Str255            errNum;
  716.     short            itemHit;
  717.  
  718.     errMsg = (StringHandle) GetResource (kErrStringType, macErr);
  719.     if (!errMsg)
  720.         errMsg = (StringHandle) GetResource (kErrStringType, kInternalError);
  721.     NumToString (macErr, errNum);
  722.     HLock ((Handle) errMsg);
  723.     ParamText (*errMsg, errNum, NULL, NULL);
  724.     InitCursor ();
  725.     itemHit = StopAlert (kErrorAlertResID, NULL);
  726.     HUnlock ((Handle) errMsg);
  727.     ReleaseResource ((Handle) errMsg);
  728. }
  729.  
  730.  
  731. /*    DisableStockPath
  732.     disable the stockPath part of the gTopLanguageModel built in AddFancierLanguageModel.
  733. */
  734.  
  735. OSErr DisableStockPath ()
  736. {
  737.     /* The stock path is the 4th item in this LM */
  738.     SRPath     stockPath;
  739.     OSErr status = SRGetIndexedItem (gTopLanguageModel, &stockPath, 3);
  740.  
  741.     if (!status) {
  742.         Boolean enabled = false;
  743.  
  744.         status = SRSetProperty (stockPath, kSREnabled, &enabled, sizeof (enabled));
  745.         
  746.         SRReleaseObject (stockPath);        /* ignore status */
  747.     }
  748.     
  749.     return status;
  750. }
  751.  
  752. /*
  753.     RefillCompanyLM
  754.     
  755.     Replace the contents of the <company> language model with different company names.    
  756. */
  757.  
  758. OSErr RefillCompanyLM ()
  759. {
  760.     /* The stock path is the 4th item in this LM */
  761.     SRPath     stockPath;
  762.     OSErr status = SRGetIndexedItem (gTopLanguageModel, &stockPath, 3);
  763.  
  764.     if (!status) {
  765.         /* empty & refill the embedded <company> language model. */
  766.         /* the companyLM is the second item in the stockPath */
  767.         SRLanguageModel companyLM;
  768.         OSErr status = SRGetIndexedItem (stockPath, &companyLM, 1);
  769.  
  770.         if (!status) {
  771.             /* this releases each phrase in the company language model */
  772.             status = SREmptyLanguageObject (companyLM);
  773.  
  774.             /* now rebuild the company language model with new companies */
  775.             if (!status) {
  776.                 const char    *kNewCompanies[]    = {    "I B M", "Motorola", "Coca Cola", NULL };
  777.                 char         **company            = (char **) kNewCompanies;
  778.                 long        refcon                = 0;
  779.  
  780.                 while (*company && !status) {
  781.                     status = SRAddText (companyLM, *company, strlen (*company), refcon++);
  782.                     ++company;
  783.                 }
  784.             }
  785.         }
  786.         status = SRReleaseObject (stockPath);        /* SRRelease… what we SRGot… */
  787.     }
  788.     
  789.     return status;
  790. }
  791.  
  792. /*    SaveTopLanguageModel
  793.     
  794.     Save the gTopLanguageModel to the current resource file.  Note that
  795.     in this case that's the resource fork of this application and is
  796.     therefore unadvisable behavior.
  797. */
  798.  
  799. OSErr SaveTopLanguageModel ()
  800. {
  801.     /* allocate a Handle of size 0 to store our language model in; */
  802.     /* SRPutLanguageObjectIntoHandle will resize it as needed */
  803.     Handle    lmHandle    = NewHandle (0);
  804.     OSErr    status        = MemError ();
  805.  
  806.     if (!status) {
  807.         status = SRPutLanguageObjectIntoHandle (gTopLanguageModel, lmHandle);
  808.         
  809.         if (!status) {
  810.             /* save the language model as a resource in the current */
  811.             /* resource file. Pick a reasonable resource type & ID */
  812.             AddResource (lmHandle, 'LMDL', 100, "\pTop Language Model");
  813.  
  814.             /* make sure it gets written to disk */
  815.             if (!(status = ResError ()))
  816.                 WriteResource (lmHandle);
  817.         }
  818.         
  819.         DisposeHandle (lmHandle);
  820.     }
  821.     
  822.     return status;
  823. }
  824.  
  825.  
  826.